unit DIrr.Dimension2d;

{$I DIrr.inc}

interface

uses
  DIrr.Types;

type
  TDimensionArray = array of Integer;

  PDimension2D = ^TDimension2D;
  TDimension2D = record
    Width: Integer;
    Height: Integer;
  public
    procedure Init(const AWidth, AHeight: Integer); {$IFDEF INLINE} inline; {$ENDIF}
    function GetArea: Integer; {$IFDEF INLINE} inline; {$ENDIF}
    function GetOptimalSize(RequirePowerOfTwo: Boolean = True; RequireSquare: Boolean = False;
      Larger: Boolean = True; MaxValue: U32 = 0): TDimension2D;
  public
    class operator Equal(Value1, Value2: TDimension2D): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator NotEqual(Value1, Value2: TDimension2D): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Divide(Value1: TDimension2D; Value2: Integer): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Multiply(Value1: TDimension2D; Value2: Integer): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: TDimension2D; Value2: Integer): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Subtract(Value1: TDimension2D; Value2: Integer): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: Integer; Value2: TDimension2D): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}

    class operator Explicit(const Value: TDimensionArray): TDimension2D; {$IFDEF INLINE} inline; {$ENDIF}
  end;

  PDimension2DF = ^TDimension2DF;
  TDimension2DF = record
    Width: Single;
    Height: Single;
  public
    procedure Init(const AWidth, AHeight: Single); {$IFDEF INLINE} inline; {$ENDIF}
    function GetArea: Single; inline;
    function GetOptimalSize(RequirePowerOfTwo: Boolean = True; RequireSquare: Boolean = False;
      Larger: Boolean = True; MaxValue: U32 = 0): TDimension2DF;
  public
    class operator Equal(Value1, Value2: TDimension2DF): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator NotEqual(Value1, Value2: TDimension2DF): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Divide(Value1: TDimension2DF; Value2: Single): TDimension2DF; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Multiply(Value1: TDimension2DF; Value2: Single): TDimension2DF; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: TDimension2DF; Value2: Single): TDimension2DF; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Subtract(Value1: TDimension2DF; Value2: Single): TDimension2DF; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: Single; Value2: TDimension2DF): TDimension2DF; {$IFDEF INLINE} inline; {$ENDIF}
  end;

  PDimension2DU = ^TDimension2DU;
  TDimension2DU = record
    Width: UInt32;
    Height: UInt32;
  public
    procedure Init(const AWidth, AHeight: UInt32); inline;
    function GetArea: UInt32; inline;
    function GetOptimalSize(RequirePowerOfTwo: Boolean = True; RequireSquare: Boolean = False;
      Larger: Boolean = True; MaxValue: U32 = 0): TDimension2DU;
    function GetAspect: F32; {$IFDEF INLINE} inline; {$ENDIF}
  public
    class operator Equal(Value1, Value2: TDimension2DU): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator NotEqual(Value1, Value2: TDimension2DU): Boolean; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Divide(Value1: TDimension2DU; Value2: UInt32): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Multiply(Value1: TDimension2DU; Value2: UInt32): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: TDimension2DU; Value2: UInt32): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Subtract(Value1: TDimension2DU; Value2: UInt32): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Add(Value1: UInt32; Value2: TDimension2DU): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}

    class operator Explicit(const Value: TDimensionArray): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
    class operator Implicit(const Value: TDimensionArray): TDimension2DU; {$IFDEF INLINE} inline; {$ENDIF}
  end;

  function Dimension2DF(const AWidth, AHeight: Single): TDimension2DF; overload; {$IFDEF INLINE} inline; {$ENDIF}
  function Dimension2DF(const Value: TDimension2DU): TDimension2DF; overload; {$IFDEF INLINE} inline; {$ENDIF}

  function Dimension2DU(const AWidth, AHeight: UInt32): TDimension2DU; overload; {$IFDEF INLINE} inline; {$ENDIF}
  function Dimension2DU(const Value: TDimension2DF): TDimension2DU; overload; {$IFDEF INLINE} inline; {$ENDIF}

implementation

uses
  Math;

function Dimension2DF(const AWidth, AHeight: Single): TDimension2DF;
begin
  Result.Width := AWidth;
  Result.Height := AHeight;
end;

function Dimension2DF(const Value: TDimension2DU): TDimension2DF;
begin
  Result.Width := Value.Width;
  Result.Height := Value.Height;
end;

function Dimension2DU(const AWidth, AHeight: UInt32): TDimension2DU;
begin
  Result.Width := AWidth;
  Result.Height := AHeight;
end;

function Dimension2DU(const Value: TDimension2DF): TDimension2DU;
begin
  Result.Width := Round(Value.Width);
  Result.Height := Round(Value.Height);
end;

{ TDimension2D }

class operator TDimension2D.Divide(Value1: TDimension2D;
  Value2: Integer): TDimension2D;
begin
  Result.Width := Value1.Width div Value2;
  Result.Height := Value1.Height div Value2;
end;

class operator TDimension2D.Multiply(Value1: TDimension2D;
  Value2: Integer): TDimension2D;
begin
  Result.Width := Value1.Width * Value2;
  Result.Height := Value1.Height * Value2;
end;

class operator TDimension2D.Add(Value1: TDimension2D;
  Value2: Integer): TDimension2D;
begin
  Result.Width := Value1.Width + Value2;
  Result.Height := Value1.Height + Value2;
end;

class operator TDimension2D.Add(Value1: Integer;
  Value2: TDimension2D): TDimension2D;
begin
  Result := Value2 + Value1;
end;

class operator TDimension2D.Equal(Value1, Value2: TDimension2D): Boolean;
begin
  Result := (Value1.Width = Value2.Width) and (Value1.Height = Value2.Height);
end;

class operator TDimension2D.Explicit(
  const Value: TDimensionArray): TDimension2D;
var
  Len: Integer;
begin
  Len := High(Value) + 1;
  if Len >= 1 then
    Result.Width := Value[0];
  if Len >= 2 then
    Result.Height := Value[1];
end;

function TDimension2D.GetArea: Integer;
begin
  Result := Width * Height;
end;

function TDimension2D.GetOptimalSize(RequirePowerOfTwo, RequireSquare,
  Larger: Boolean; MaxValue: U32): TDimension2D;
var
  i, j: U32;
begin
  i := 1;
  j := 1;
  if RequirePowerOfTwo then
  begin
    while (i < U32(Width)) do
      i := i shl 1;
    if (not Larger) and (i <> 1) and (i <> U32(Width)) then
      i := i shr 1;
    while (j < U32(Height)) do
      j := j shl 1;
    if (not Larger) and (j <> 1) and (j <> U32(Width)) then
      j := j shr 1;
  end
  else
  begin
    i := U32(Width);
    j := U32(Height);
  end;

  if RequireSquare then
  begin
    if (Larger and (i > j)) or ((not Larger) and (i < j)) then
      j := i
    else
      i := j;
  end;

  if (MaxValue > 0) and (i > MaxValue) then
    i := MaxValue;
  if (MaxValue > 0) and (j > MaxValue) then
    j := MaxValue;

  Result.Init(i, j);
end;

procedure TDimension2D.Init(const AWidth, AHeight: Integer);
begin
  Width := AWidth;
  Height := AHeight;
end;

class operator TDimension2D.NotEqual(Value1, Value2: TDimension2D): Boolean;
begin
  Result := (Value1.Width <> Value2.Width) or (Value1.Height = Value2.Height);
end;

class operator TDimension2D.Subtract(Value1: TDimension2D;
  Value2: Integer): TDimension2D;
begin
  Result.Width := Value1.Width - Value2;
  Result.Height := Value1.Height - Value2;
end;

{ TDimension2DF }

class operator TDimension2DF.Divide(Value1: TDimension2DF;
  Value2: Single): TDimension2DF;
begin
  Result.Width := Value1.Width / Value2;
  Result.Height := Value1.Height / Value2;
end;

class operator TDimension2DF.Multiply(Value1: TDimension2DF;
  Value2: Single): TDimension2DF;
begin
  Result.Width := Value1.Width * Value2;
  Result.Height := Value1.Height * Value2;
end;

class operator TDimension2DF.Add(Value1: TDimension2DF;
  Value2: Single): TDimension2DF;
begin
  Result.Width := Value1.Width + Value2;
  Result.Height := Value1.Height + Value2;
end;

class operator TDimension2DF.Add(Value1: Single;
  Value2: TDimension2DF): TDimension2DF;
begin
  Result := Value2 + Value1;
end;

class operator TDimension2DF.Equal(Value1, Value2: TDimension2DF): Boolean;
begin
  Result := (Value1.Width = Value2.Width) and (Value1.Height = Value2.Height);
end;

function TDimension2DF.GetArea: Single;
begin
  Result := Width * Height;
end;

function TDimension2DF.GetOptimalSize(RequirePowerOfTwo, RequireSquare,
  Larger: Boolean; MaxValue: U32): TDimension2DF;
var
  i, j, tw, th: U32;
begin
  i := 1;
  j := 1;

  tw := U32(Ceil(Width));
  th := U32(Ceil(Height));

  if RequirePowerOfTwo then
  begin
    while (i < tw) do
      i := i shl 1;
    if (not Larger) and (i <> 1) and (i <> tw) then
      i := i shr 1;
    while (j < th) do
      j := j shl 1;
    if (not Larger) and (j <> 1) and (j <> th) then
      j := j shr 1;
  end
  else
  begin
    i := tw;
    j := th;
  end;

  if RequireSquare then
  begin
    if (Larger and (i > j)) or ((not Larger) and (i < j)) then
      j := i
    else
      i := j;
  end;

  if (MaxValue > 0) and (i > MaxValue) then
    i := MaxValue;
  if (MaxValue > 0) and (j > MaxValue) then
    j := MaxValue;

  Result.Init(i, j);
end;

procedure TDimension2DF.Init(const AWidth, AHeight: Single);
begin
  Width := AWidth;
  Height := AHeight;
end;

class operator TDimension2DF.NotEqual(Value1, Value2: TDimension2DF): Boolean;
begin
  Result := (Value1.Width <> Value2.Width) or (Value1.Height = Value2.Height);
end;

class operator TDimension2DF.Subtract(Value1: TDimension2DF;
  Value2: Single): TDimension2DF;
begin
  Result.Width := Value1.Width - Value2;
  Result.Height := Value1.Height - Value2;
end;

{ TDimension2DU }

class operator TDimension2DU.Divide(Value1: TDimension2DU;
  Value2: UInt32): TDimension2DU;
begin
  Result.Width := Value1.Width div Value2;
  Result.Height := Value1.Height div Value2;
end;

class operator TDimension2DU.Multiply(Value1: TDimension2DU;
  Value2: UInt32): TDimension2DU;
begin
  Result.Width := Value1.Width * Value2;
  Result.Height := Value1.Height * Value2;
end;

class operator TDimension2DU.Add(Value1: TDimension2DU;
  Value2: UInt32): TDimension2DU;
begin
  Result.Width := Value1.Width + Value2;
  Result.Height := Value1.Height + Value2;
end;

class operator TDimension2DU.Add(Value1: UInt32;
  Value2: TDimension2DU): TDimension2DU;
begin
  Result := Value2 + Value1;
end;

class operator TDimension2DU.Equal(Value1, Value2: TDimension2DU): Boolean;
begin
  Result := (Value1.Width = Value2.Width) and (Value1.Height = Value2.Height);
end;

class operator TDimension2DU.Explicit(
  const Value: TDimensionArray): TDimension2DU;
var
  Len: Integer;
begin
  Len := High(Value) + 1;
  if Len >= 1 then
    Result.Width := Value[0];
  if Len >= 2 then
    Result.Height := Value[1];
end;

function TDimension2DU.GetArea: UInt32;
begin
  Result := Width * Height;
end;

function TDimension2DU.GetAspect: F32;
begin
  Result := Width / Height;
end;

function TDimension2DU.GetOptimalSize(RequirePowerOfTwo, RequireSquare,
  Larger: Boolean; MaxValue: U32): TDimension2DU;
var
  i, j: U32;
begin
  i := 1;
  j := 1;
  if RequirePowerOfTwo then
  begin
    while (i < Width) do
      i := i shl 1;
    if (not Larger) and (i <> 1) and (i <> Width) then
      i := i shr 1;
    while (j < Height) do
      j := j shl 1;
    if (not Larger) and (j <> 1) and (j <> Width) then
      j := j shr 1;
  end
  else
  begin
    i := Width;
    j := Height;
  end;

  if RequireSquare then
  begin
    if (Larger and (i > j)) or ((not Larger) and (i < j)) then
      j := i
    else
      i := j;
  end;

  if (MaxValue > 0) and (i > MaxValue) then
    i := MaxValue;
  if (MaxValue > 0) and (j > MaxValue) then
    j := MaxValue;

  Result.Init(i, j);
end;

class operator TDimension2DU.Implicit(
  const Value: TDimensionArray): TDimension2DU;
var
  Len: Integer;
begin
  Len := High(Value) + 1;
  if Len >= 1 then
    Result.Width := Value[0];
  if Len >= 2 then
    Result.Height := Value[1];
end;

procedure TDimension2DU.Init(const AWidth, AHeight: UInt32);
begin
  Width := AWidth;
  Height := AHeight;
end;

class operator TDimension2DU.NotEqual(Value1, Value2: TDimension2DU): Boolean;
begin
  Result := (Value1.Width <> Value2.Width) or (Value1.Height = Value2.Height);
end;

class operator TDimension2DU.Subtract(Value1: TDimension2DU;
  Value2: UInt32): TDimension2DU;
begin
  Result.Width := Value1.Width - Value2;
  Result.Height := Value1.Height - Value2;
end;

end.
